function randRange(start, end) {
    let range = end - start + 1;
    return Math.floor((Math.random() * range) + start);
}

function shuffle(elements) {
    let length = elements.length;
    for (let i = 0; i < length - 1; i++) {
        let j = randRange(i, length - 1);
        let tmp = elements[i];
        elements[i] = elements[j];
        elements[j] = tmp;
    }
}

function choice(elements) {
    let n = randRange(0, elements.length - 1);
    return elements[n];
}

function repeateNTimes(n, fn) {
    let result = [];
    for (let i = 0; i < n; i++) {
        result.push(fn.apply(null, Array.prototype.slice.call(arguments, 2)));
    }
    return result;
}

function group(elements) {
    let result = {};
    for (let index = 0; index < elements.length; index++) {
        let value = elements[index];
        if (result[value] === undefined) {
            result[value] = 1;
            continue;
        }
        result[value] += 1;
    }
    return result;
}

function pdf2cdf(pdf) {
    let cdf = pdf.slice();
    for (let i = 1; i < cdf.length - 1; i++) {
        cdf[i] += cdf[i - 1];
    }
    cdf[cdf.length - 1] = 1.0;
    return cdf;
}

function discreteSampling(probs) {
    let events = [];
    let pdf = [];
    for (let key = 0; key < probs.length; key++) {
        events.push(key);
        pdf.push(probs[key]);   
    }
    let cdf = pdf2cdf(pdf);
    let r = Math.random();
    for (let x = 0; x < cdf.length; x++) {
        if (r < cdf[x]) {
            return events[x];
        }
    }
    return events[events.length - 1];
}

exports.shuffle = shuffle;
exports.randRange = randRange;
exports.choice = choice;
exports.discreteSampling = discreteSampling;
exports.group = group;

